package com.zzz.system.service.exam.impl;

import cn.hutool.core.collection.CollectionUtil;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.github.pagehelper.PageHelper;
import com.zzz.common.core.enums.ResultCode;
import com.zzz.common.security.exception.ServiceException;
import com.zzz.system.domain.exam.Exam;
import com.zzz.system.domain.exam.ExamQuestion;
import com.zzz.system.domain.exam.dto.ExamAddDTO;
import com.zzz.system.domain.exam.dto.ExamEditDTO;
import com.zzz.system.domain.exam.dto.ExamQueryDTO;
import com.zzz.system.domain.exam.dto.ExamQuestionAddDTO;
import com.zzz.system.domain.exam.vo.ExamDetailVO;
import com.zzz.system.domain.exam.vo.ExamVO;
import com.zzz.system.domain.question.Question;
import com.zzz.system.domain.question.vo.QuestionVo;
import com.zzz.system.manager.ExamCacheManager;
import com.zzz.system.mapper.exam.ExamMapper;
import com.zzz.system.mapper.exam.ExamQuestionMapper;
import com.zzz.system.mapper.question.QuestionMapper;
import com.zzz.system.service.exam.ExamService;
import com.zzz.common.core.constants.Constants;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.BeanUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

import java.time.LocalDateTime;
import java.util.ArrayList;
import java.util.LinkedHashSet;
import java.util.List;
import java.util.Set;

/**
 * @BelongsProject: oj-project
 * @BelongsPackage: com.zzz.system.service.exam.impl
 * @Author: zhai
 * @CreateTime: 2025-10-19  16:29
 * @Description: 竞赛服务
 * @Version: 1.0
 */
@Slf4j
@Service
public class ExamServiceImpl extends ServiceImpl<ExamQuestionMapper, ExamQuestion> implements ExamService {

    @Autowired
    private ExamMapper examMapper;

    @Autowired
    private QuestionMapper questionMapper;

    @Autowired
    private ExamQuestionMapper examQuestionMapper;

    @Autowired
    private ExamCacheManager examCacheManager;

    /**
     * @description: 获取竞赛列表
     * @author: zhai
     * @date: 2025/10/22 18:55
     * @param: [examQueryDTO]
     * @return: java.util.List<com.zzz.system.domain.exam.vo.ExamVO>
     **/
    @Override
    public List<ExamVO> list(ExamQueryDTO examQueryDTO) {
        PageHelper.startPage(examQueryDTO.getPageNum(), examQueryDTO.getPageSize());
        List<ExamVO> examList = examMapper.list(examQueryDTO);
        return examList;
    }

    /**
     * @description: 添加竞赛
     * @author: zhai
     * @date: 2025/10/22 18:55
     * @param: [examAddDTO]
     * @return: int
     **/
    @Override
    public int add(ExamAddDTO examAddDTO) {
        log.info("添加竞赛");
        List<Exam> examList = examMapper.selectList(new LambdaQueryWrapper<Exam>()
                .eq(Exam::getTitle, examAddDTO.getTitle()));
        if (CollectionUtil.isNotEmpty(examList)) {
            throw new ServiceException(ResultCode.FAILED_ALREADY_EXISTS);
        }
        if (examAddDTO.getStartTime().isBefore(LocalDateTime.now())) {
            throw new ServiceException(ResultCode.EXAM_START_TIME_BEFORE_CURRENT_TIME);  //竞赛开始时间不能早于当前时间
        }
        if (examAddDTO.getStartTime().isAfter(examAddDTO.getEndTime())) {
            throw new ServiceException(ResultCode.EXAM_START_TIME_AFTER_END_TIME);
        }
        Exam exam = new Exam();
        BeanUtils.copyProperties(examAddDTO, exam);
        return examMapper.insert(exam);
    }

    /**
     * @description: 添加竞赛题
     * @author: zhai
     * @date: 2025/10/22 18:55
     * @param: [examQuestionAddDTO]
     * @return: boolean
     **/
    @Override
    @Transactional(rollbackFor = Exception.class)
    public boolean addExamQuestion(ExamQuestionAddDTO examQuestionAddDTO) {
        log.info("参数:{}", examQuestionAddDTO);
        // 校验竞赛是否存在
        Exam exam = getExam(examQuestionAddDTO.getExamId());
        extractedExam(exam);
        log.info("获取参数，竞赛id：{}", exam.getExamId());

        // 校验竞赛题是否为空,使用questionIdSet存储每道赛题
        LinkedHashSet<Long> questionIdSet = examQuestionAddDTO.getQuestionIdSet();
        if (CollectionUtil.isEmpty(questionIdSet)) {
            // 这边赛题列表可以为空，只要未发布即可
            return true;
        }
        log.info("获取参数，每道题id：{}", questionIdSet);
        // 校验每道题是否存在
        List<Question> questionList = questionMapper.selectBatchIds(questionIdSet);
        if (questionList == null || questionList.size() < questionIdSet.size()) {
            throw new ServiceException(ResultCode.EXAM_QUESTION_NOT_EXISTS);
        }
        // 插入每一道题
        return saveExamQuestion(exam, questionIdSet);
    }

    /**
     * 删除竞赛赛题
     * @param examId
     * @param questionId
     * @return
     */
    @Override
    public int questionDelete(Long examId, Long questionId) {
        Exam exam = getExam(examId);
        checkExam(exam);
        Question question = questionMapper.selectById(questionId);
        if (question == null) {
            throw new ServiceException(ResultCode.EXAM_QUESTION_NOT_EXISTS);
        }
        // 判断竞赛的状态
        if (Constants.TRUE.equals(exam.getStatus())) {
            throw new ServiceException(ResultCode.EXAM_IS_PUBLISH);
        }
        return examQuestionMapper.delete(new LambdaQueryWrapper<ExamQuestion>()
                .eq(ExamQuestion::getExamId, examId)
                .eq(ExamQuestion::getQuestionId, questionId));
    }

    /**
     * @description: 获取竞赛详情
     * @author: zhai
     * @date: 2025/10/22 18:56
     * @param: [examId]
     * @return: com.zzz.system.domain.exam.vo.ExamDetailVO
     **/
    @Override
    public ExamDetailVO detail(Long examId) {
        log.info("获得参数，竞赛id：{}", examId);
        ExamDetailVO examDetailVO = new ExamDetailVO();
        Exam exam = getExam(examId);
        BeanUtils.copyProperties(exam, examDetailVO);
        List<QuestionVo> questionVoList = examQuestionMapper.selectExamQuestionList(examId);
        if (CollectionUtil.isEmpty(questionVoList)) {
            return examDetailVO;
        }
        examDetailVO.setExamQuestionList(questionVoList);
        return examDetailVO;
    }

    /**
     * @description: 删除竞赛
     * @author: zhai
     * @date: 2025/10/22 18:55
     * @param: [examId]
     * @return: int
     **/
    @Override
    public int delete(Long examId) {
        Exam exam = getExam(examId);
        checkExam(exam);
        // 先删除tb_exam_question在删除tb_exam
        examQuestionMapper.delete(new LambdaQueryWrapper<ExamQuestion>()
                .eq(ExamQuestion::getExamId, examId));
        return examMapper.deleteById(examId);
    }

    /**
     * @description: 修改竞赛
     * @author: zhai
     * @date: 2025/10/22 18:56
     * @param: [examEditDTO]
     * @return: int
     **/
    @Override
    public int edit(ExamEditDTO examEditDTO) {
        Exam exam = getExam(examEditDTO.getExamId());
        if (Constants.TRUE.equals(exam.getStatus())) {
            throw new ServiceException(ResultCode.EXAM_IS_PUBLISH);
        }
        checkExam(exam);
        checkExamSaveParams(examEditDTO, examEditDTO.getExamId());
        exam.setTitle(examEditDTO.getTitle());
        exam.setStartTime(examEditDTO.getStartTime());
        exam.setEndTime(examEditDTO.getEndTime());
        return examMapper.updateById(exam);
    }

    /**
     * @description: 发布竞赛
     * @author: zhai
     * @date: 2025/10/22 20:11
     * @param: [examId]
     * @return: int
     **/
    @Override
    public int publish(Long examId) {
        Exam exam = getExam(examId);
        if (exam.getEndTime().isBefore(LocalDateTime.now())) {
            log.error(ResultCode.EXAM_IS_FINISH.getMsg());
            throw new ServiceException(ResultCode.EXAM_IS_FINISH);
        }
        Long count = examQuestionMapper.selectCount(new LambdaQueryWrapper<ExamQuestion>()
                .eq(ExamQuestion::getExamId, examId));
        if (count == null || count <= 0) {
            log.error("没有赛题不能发布，请添加赛题后在发布");
            throw new ServiceException(ResultCode.EXAM_NOT_HAS_QUESTION);
        }
        exam.setStatus(Constants.TRUE);
        // 在这里将发布的竞赛信息存储到redis中
        examCacheManager.addCache(exam);
        return examMapper.updateById(exam);
    }

    /**
     * @description: 取消发布竞赛
     * @author: zhai
     * @date: 2025/10/22 20:12
     * @param: [examId]
     * @return: int
     **/
    @Override
    public int cancelPublish(Long examId) {
        Exam exam = getExam(examId);
        checkExam(exam);
        if (Constants.FALSE.equals(exam.getStatus())) {
            log.error("取消发布失败，请勿重复操作");
            throw new ServiceException(ResultCode.EXAM_IS_NOT_PUBLISH);
        }
        exam.setStatus(com.zzz.common.core.constants.Constants.FALSE);
        examCacheManager.deleteCache(exam);
        return examMapper.updateById(exam);
    }

    // 校验竞赛是否存在
    private void extractedExam(Exam exam) {
        if (exam == null) {
            log.error(ResultCode.EXAM_NOT_EXISTS.getMsg());
            throw new ServiceException(ResultCode.EXAM_NOT_EXISTS);
        }
    }

    // 保存竞赛题到对应的表中
    private boolean saveExamQuestion(Exam exam, Set<Long> questionIdSet) {
        int num = 1;
        log.info("exam的参数是：{}", exam);
        List<ExamQuestion> examQuestionList = new ArrayList<>();
        for (Long questionId : questionIdSet) {
            ExamQuestion examQuestion = new ExamQuestion();
            log.info("添加竞赛题的id是：{}", exam.getExamId());
            examQuestion.setExamId(exam.getExamId());
            examQuestion.setQuestionId(questionId);
            examQuestion.setQuestionOrder(num++);
            examQuestionList.add(examQuestion);
            log.info("添加竞赛题：{}", examQuestion);
        }
        return saveBatch(examQuestionList);
    }

    // 获取竞赛
    private Exam getExam(Long examId) {
        Exam exam = examMapper.selectById(examId);
        if (exam == null) {
            throw new ServiceException(ResultCode.EXAM_NOT_EXISTS);
        }
        return exam;
    }

    // 校验竞赛是否开始
    private void checkExam(Exam exam) {
        if (exam.getStartTime().isBefore(LocalDateTime.now())) {
            log.error(ResultCode.EXAM_STARTED.getMsg());
            throw new ServiceException(ResultCode.EXAM_STARTED);
        }
    }

    // 检查竞赛保存参数
    private void checkExamSaveParams(ExamAddDTO examSaveDTO, Long examId) {
        //1、竞赛标题是否重复进行判断   2、竞赛开始、结束时间进行判断
        List<Exam> examList = examMapper
                .selectList(new LambdaQueryWrapper<Exam>()
                        .eq(Exam::getTitle, examSaveDTO.getTitle())
                        .ne(examId != null, Exam::getExamId, examId));
        if (CollectionUtil.isNotEmpty(examList)) {
            throw new ServiceException(ResultCode.FAILED_ALREADY_EXISTS);
        }
        if (examSaveDTO.getStartTime().isBefore(LocalDateTime.now())) {
            throw new ServiceException(ResultCode.EXAM_START_TIME_BEFORE_CURRENT_TIME);  //竞赛开始时间不能早于当前时间
        }
        if (examSaveDTO.getStartTime().isAfter(examSaveDTO.getEndTime())) {
            throw new ServiceException(ResultCode.EXAM_START_TIME_AFTER_END_TIME);
        }
    }
}